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.
Files changed (5) hide show
  1. data/README +109 -0
  2. data/lib/app-ctx.rb +228 -0
  3. data/lib/app-ctx/dlog.rb +164 -0
  4. data/test/t_app-ctx.rb +199 -0
  5. 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:
@@ -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
@@ -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
@@ -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
+